package cn.smile.business.core.impl;

import cn.smile.bean.dto.core.ResInfoDTO;
import cn.smile.bean.dto.core.ResTreeDTO;
import cn.smile.bean.dto.core.RoleInfoDTO;
import cn.smile.bean.entity.core.ResInfo;
import cn.smile.bean.entity.core.ResRole;
import cn.smile.bean.form.core.res.ResAddForm;
import cn.smile.bean.form.core.res.ResQueryForm;
import cn.smile.bean.form.core.res.ResUpdateForm;
import cn.smile.bean.form.core.role.RoleQueryForm;
import cn.smile.business.core.IResService;
import cn.smile.business.core.IRoleService;
import cn.smile.commons.constant.CommonConstant;
import cn.smile.commons.constant.NumberConstant;
import cn.smile.commons.domain.BaseServiceImpl;
import cn.smile.commons.exceptions.BusinessException;
import cn.smile.commons.response.MyResponseCode;
import cn.smile.repository.core.ResInfoMapper;
import cn.smile.repository.core.ResRoleMapper;
import cn.smile.utils.BeanCopy;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author smile-jt
 */
@Slf4j
@Service
public class ResServiceImpl extends BaseServiceImpl<ResInfoMapper, ResInfo> implements IResService {
    @Resource
    private ResRoleMapper relMapper;
    @Resource
    private IRoleService roleService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addInfo(ResAddForm form) {
        log.info("[ResServiceImpl].[addInfo] ------> Add Res Info Start, form = {}", JSON.toJSONString(form));
        this.checkUri(form.getResUri());
        ResInfo entity = BeanCopy.copyBean(form, ResInfo.class);
        super.addBaseCol(entity);
        int insertNum = baseMapper.insert(entity);
        log.info("[ResServiceImpl].[addInfo] ------> Add Res Info End, insertNum = {}", insertNum);
        return insertNum > NumberConstant.ZERO;
    }

    @Override
    public Boolean updateInfo(ResUpdateForm form) {
        log.info("[ResServiceImpl].[updateInfo] ------> Update Res Info Start, form = {}", JSON.toJSONString(form));
        ResInfo entity = BeanCopy.copyBean(form, ResInfo.class);
        super.updateById(entity);
        int updateNum = baseMapper.insert(entity);
        log.info("[ResServiceImpl].[updateInfo] ------> Update Res Info End, updateNum = {}", updateNum);
        return updateNum > NumberConstant.ZERO;
    }

    @Override
    public Set<String> queryUriByRoleIds(List<String> roleCodes) {
        log.info("[ResServiceImpl].[queryUriByRoleIds] ------> Query Res Uri By Role Ids Start, roleCodes = {}", JSON.toJSONString(roleCodes));
        Set<String> resSet = new HashSet<>(NumberConstant.EIGHT);
        RoleQueryForm form = new RoleQueryForm();
        form.setRoleCodes(roleCodes);
        List<RoleInfoDTO> roleList = roleService.queryList(form);
        if (!CollectionUtils.isEmpty(roleList)) {
            List<String> roleIds = Lists.newArrayList();
            roleList.forEach(role -> roleIds.add(role.getId()));
            List<ResRole> resRoles = relMapper.selectList(new QueryWrapper<ResRole>().lambda().eq(ResRole::getDeleted, Boolean.FALSE).in(ResRole::getRoleId, roleIds));
            if (!CollectionUtils.isEmpty(resRoles)) {
                List<String> ids = Lists.newArrayList();
                resRoles.forEach(resRole -> ids.add(resRole.getResId()));
                List<ResInfo> resInfos = baseMapper.selectList(new QueryWrapper<ResInfo>().lambda().eq(ResInfo::getDeleted, Boolean.FALSE).in(ResInfo::getId, ids).ne(ResInfo::getParentId, CommonConstant.ROOT_PARENT_ID));
                if (!CollectionUtils.isEmpty(resInfos)) {
                    resInfos.forEach(res -> resSet.add(res.getResUri()));
                }
            }
        }
        log.info("[ResServiceImpl].[queryUriByRoleIds] ------> Query Res Uri By Role Ids End");
        return resSet;
    }

    @Override
    public Set<String> getMenuCodeByRole(List<String> roleIds) {
        log.info("[ResServiceImpl].[getMenuCodeByRole] ------> 根据角色 Code 查询菜单 Code集合 Start, roleIds = {}", JSON.toJSONString(roleIds));
        Set<String> menuCodes = new HashSet<>(NumberConstant.EIGHT);
        List<ResRole> resRoles = relMapper.selectList(new QueryWrapper<ResRole>().lambda().eq(ResRole::getDeleted, Boolean.FALSE).in(ResRole::getRoleId, roleIds));
        if (!CollectionUtils.isEmpty(resRoles)) {
            List<String> resIds = resRoles.stream().map(ResRole::getResId).collect(Collectors.toList());
            List<ResInfo> resList = baseMapper.selectList(new QueryWrapper<ResInfo>().lambda().eq(ResInfo::getDeleted, Boolean.FALSE).in(ResInfo::getId, resIds));
            if (!CollectionUtils.isEmpty(resList)) {
                resList.forEach(res -> menuCodes.add(res.getMenuCode()));
            }
        }
        log.info("[ResServiceImpl].[getMenuCodeByRole] ------> 根据角色 Code 查询菜单 Code集合 End, menuCodes = {}", JSON.toJSONString(menuCodes));
        return menuCodes;
    }

    @Override
    public List<ResTreeDTO> getResTree() {
        log.info("[ResServiceImpl].[getResTree] ------> 获取资源树 Start");
        List<ResTreeDTO> resTree = Lists.newArrayList();
        List<ResInfo> resList = baseMapper.selectList(new QueryWrapper<ResInfo>().lambda().eq(ResInfo::getDeleted, Boolean.FALSE).orderByAsc(ResInfo::getSortNum));
        Map<String, List<ResTreeDTO>> resMap = Maps.newHashMap();
        if (!CollectionUtils.isEmpty(resList)) {
            for (ResInfo res : resList) {
                ResTreeDTO dto = BeanCopy.copyBean(res, ResTreeDTO.class);
                dto.setChildren(Lists.newArrayList());
                if (!resMap.containsKey(dto.getParentId())) {
                    resMap.put(dto.getParentId(), Lists.newArrayList());
                }
                resMap.get(res.getParentId()).add(dto);
                if (res.getParentId().equals(CommonConstant.ROOT_PARENT_ID)) {
                    resTree.add(dto);
                }
            }
            //这个资源树只有两层，所以不用递归
            for (ResTreeDTO dto : resTree) {
                dto.setChildren(resMap.getOrDefault(dto.getId(), Lists.newArrayList()));
            }
        }
        log.info("[ResServiceImpl].[getResTree] ------> 获取资源树 End");
        return resTree;
    }

    @Override
    public Map<String, List<String>> getResCodeMap(List<String> roleIds) {
        log.info("[ResServiceImpl].[getResCodeMap] ------> 查询角色对应资源Code Start");
        Map<String, List<String>> resCodeMap = Maps.newHashMap();
        //查询所有资源
        List<ResInfo> resList = baseMapper.selectList(new QueryWrapper<ResInfo>().lambda().eq(ResInfo::getDeleted, Boolean.FALSE));
        Map<String, String> resMap = Maps.newHashMap();
        if (!CollectionUtils.isEmpty(resList)) {
            resList.forEach(res -> resMap.put(res.getId(), res.getMenuCode()));
        }
        //查询角色对应资源
        List<ResRole> resRoles = relMapper.selectList(new QueryWrapper<ResRole>().lambda().in(ResRole::getRoleId, roleIds).eq(ResRole::getDeleted, Boolean.FALSE));
        if (!CollectionUtils.isEmpty(resRoles)) {
            for (ResRole resRole : resRoles) {
                if (!resCodeMap.containsKey(resRole.getRoleId())) {
                    resCodeMap.put(resRole.getRoleId(), Lists.newArrayList());
                }
                if (resMap.containsKey(resRole.getResId())) {
                    resCodeMap.get(resRole.getRoleId()).add(resMap.get(resRole.getResId()));
                }
            }
        }
        log.info("[ResServiceImpl].[getResCodeMap] ------> 查询角色对应资源Code End");
        return resCodeMap;
    }

    @Override
    public void saveResByRole(List<String> resCodes, String roleId) {
        log.info("[ResServiceImpl].[saveResByRole] ------> 保存角色对应资源关系 Start");
        //删除角色对应资源关系
        relMapper.delete(new QueryWrapper<ResRole>().lambda().eq(ResRole::getRoleId, roleId));
        //保存角色对应资源关系
        if (!CollectionUtils.isEmpty(resCodes)) {
            List<ResInfo> resInfos = baseMapper.selectList(new QueryWrapper<ResInfo>().lambda().eq(ResInfo::getDeleted, Boolean.FALSE).in(ResInfo::getMenuCode, resCodes));
            if (!CollectionUtils.isEmpty(resInfos)) {
                resInfos.forEach(resInfo -> {
                    ResRole resRole = new ResRole();
                    resRole.setRoleId(roleId);
                    resRole.setResId(resInfo.getId());
                    resRole.setDeleted(Boolean.FALSE);
                    resRole.setCreateTime(LocalDateTime.now());
                    resRole.setUpdateTime(LocalDateTime.now());
                    relMapper.insert(resRole);
                });
            }
        }
        log.info("[ResServiceImpl].[saveResByRole] ------> 保存角色对应资源关系 End");
    }

    @Override
    public IPage<ResInfoDTO> queryPage(ResQueryForm form) {
        log.info("[ResServiceImpl].[queryPage] ------> 分页查询菜单资源 Start, form = {}", JSON.toJSONString(form));
        LambdaQueryWrapper<ResInfo> qw = this.getQueryParams(form);
        Page<ResInfo> resPage = baseMapper.selectPage(form.queryPage(), qw);
        IPage<ResInfoDTO> result = BeanCopy.copyPage(resPage, ResInfoDTO.class);
        this.setDetail(result.getRecords());
        log.info("[ResServiceImpl].[queryPage] ------> 分页查询菜单资源 End");
        return result;
    }

    @Override
    public List<ResInfoDTO> queryAllTopMenu() {
        log.info("[ResServiceImpl].[queryAllTopMenu] ------> 查询所有顶级菜单 Start");
        LambdaQueryWrapper<ResInfo> qw = new LambdaQueryWrapper<ResInfo>().eq(ResInfo::getDeleted, Boolean.FALSE).eq(ResInfo::getIsShow, Boolean.TRUE).eq(ResInfo::getParentId, CommonConstant.ROOT_PARENT_ID).orderByAsc(ResInfo::getSortNum);
        List<ResInfo> resList = baseMapper.selectList(qw);
        List<ResInfoDTO> result = BeanCopy.copyList(resList, ResInfoDTO.class);
        if (CollectionUtils.isEmpty(resList)) {
            result = Lists.newArrayList();
        }
        ResInfoDTO topDTO = new ResInfoDTO();
        topDTO.setId(CommonConstant.ROOT_PARENT_ID);
        topDTO.setMenuName(CommonConstant.ROOT_PARENT_SELECT_NAME);
        result.add(NumberConstant.ZERO, topDTO);
        log.info("[ResServiceImpl].[queryAllTopMenu] ------> 查询所有顶级菜单 End");
        return result;
    }

    @Override
    public Boolean delResByWeb(String resId) {
        log.info("[ResServiceImpl].[delResByWeb] ------> 删除资源 Start, resId = {}", resId);
        //删除资源
        ResInfo entity = baseMapper.selectOne(new QueryWrapper<ResInfo>().lambda().eq(ResInfo::getId, resId).eq(ResInfo::getDeleted, Boolean.FALSE));
        if (ObjectUtils.isEmpty(entity)) {
            throw new BusinessException(MyResponseCode.RESULT_DATA_NONE);
        }
        if (entity.getSystemAdd()) {
            throw new BusinessException(MyResponseCode.DELETE_SYSTEM_RESOURCES_ERROR);
        }
        super.delBaseCol(entity);
        int deleteNum = baseMapper.updateById(entity);
        if (deleteNum > NumberConstant.ZERO) {
            //删除资源角色关系
            relMapper.delete(new QueryWrapper<ResRole>().lambda().eq(ResRole::getResId, resId));
        }
        log.info("[ResServiceImpl].[delResByWeb] ------> 删除资源 End, deleteNum = {}", deleteNum);
        return deleteNum > NumberConstant.ZERO;
    }

    /**
     * 写入详细信息
     *
     * @param dataList 数据集合
     */
    private void setDetail(List<ResInfoDTO> dataList) {
        if (!CollectionUtils.isEmpty(dataList)) {
            for (ResInfoDTO dto : dataList) {
                dto.setSystemFalg(dto.getSystemAdd() ? CommonConstant.YES : CommonConstant.NO);
            }
        }
    }

    /**
     * 查询条件参数
     *
     * @param form 查询参数
     * @return 查询对象
     */
    private LambdaQueryWrapper<ResInfo> getQueryParams(ResQueryForm form) {
        LambdaQueryWrapper<ResInfo> qw = new QueryWrapper<ResInfo>().lambda();
        qw.eq(ResInfo::getIsShow, Boolean.TRUE);
        qw.eq(ResInfo::getDeleted, Boolean.FALSE);
        if (!ObjectUtils.isEmpty(form.getMenuName())) {
            qw.like(ResInfo::getMenuName, form.getMenuName());
        }
        if (!ObjectUtils.isEmpty(form.getTopId())) {
            qw.eq(ResInfo::getParentId, form.getTopId());
        }
        return qw;
    }

    /**
     * 检查URI是否存在
     *
     * @param uri 资源URI
     */
    private void checkUri(String uri) {
        List<ResInfo> resInfos = baseMapper.selectList(new QueryWrapper<ResInfo>().lambda().eq(ResInfo::getDeleted, Boolean.FALSE).eq(ResInfo::getResUri, uri));
        if (!CollectionUtils.isEmpty(resInfos)) {
            throw new BusinessException(MyResponseCode.URI_ROLE_EXIST_ERROR);
        }
    }
}
